package com.xwj.seckill.service.impl;

import com.xwj.common.dto.BaseResponse;
import com.xwj.seckill.annotation.RedisServiceLock;
import com.xwj.seckill.annotation.ZookeeperServiceLock;
import com.xwj.seckill.dao.BookRobMapper;
import com.xwj.seckill.dao.BookStoreMapper;
import com.xwj.seckill.domain.BookRob;
import com.xwj.seckill.domain.BookStore;
import com.xwj.seckill.dto.BookRobDto;
import com.xwj.seckill.service.BookRobService;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.redisson.Redisson;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author xwj
 * @date 2020/5/6
 */
@Service
public class BookRobServiceImpl implements BookRobService {
    @Autowired
    private BookRobMapper bookRobMapper;
    @Autowired
    private BookStoreMapper bookStoreMapper;
    @Autowired
    private CuratorFramework client;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse robWithNoLock(BookRobDto dto) {
        BookStore bookStore = bookStoreMapper.selectByBookNo(dto.getBookNo());
        // 库存充足且当前用户未抢购过
        if (bookStore != null && bookStore.getStore() > 0) {
            Integer count = bookRobMapper.countByBookNoUserId(dto.getUserId(), dto.getBookNo());
            if (count <= 0) {
                // 更新库存记录
                int res = bookStoreMapper.updateStore(dto.getBookNo());
                if (res > 0) {
                    BookRob bookRob = new BookRob();
                    bookRob.setBookNo(dto.getBookNo());
                    bookRob.setUserId(dto.getUserId());
                    bookRob.setCreateTime(LocalDateTime.now());
                    bookRobMapper.insertSelective(bookRob);
                    System.out.println("抢购成功：" + bookRob.toString());
                    return BaseResponse.success("抢购成功");
                }
            }
        }
        return BaseResponse.error("抢购结束");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse robWithZookeeperLock(BookRobDto dto) {
        final String path = "/distribute/book/lock/" + dto.getBookNo();
        InterProcessMutex mutex = new InterProcessMutex(client, path);
        try {
            if (mutex.acquire(10L, TimeUnit.SECONDS)) {
                BookStore bookStore = bookStoreMapper.selectByBookNo(dto.getBookNo());
                // 库存充足且当前用户未抢购过
                if (bookStore != null && bookStore.getStore() > 0) {
                    Integer count = bookRobMapper.countByBookNoUserId(dto.getUserId(), dto.getBookNo());
                    if (count <= 0) {
                        // 更新库存记录
                        int res = bookStoreMapper.updateStore(dto.getBookNo());
                        if (res > 0) {
                            BookRob bookRob = new BookRob();
                            bookRob.setBookNo(dto.getBookNo());
                            bookRob.setUserId(dto.getUserId());
                            bookRob.setCreateTime(LocalDateTime.now());
                            bookRobMapper.insertSelective(bookRob);
                            System.out.println("抢购成功：" + bookRob.toString());
                            return BaseResponse.success("抢购成功");
                        }
                    }
                }
                return BaseResponse.error("抢购结束");
            } else {
                return BaseResponse.error("抢购失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                mutex.release();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return BaseResponse.error("抢购失败");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse robWithRedisLock(BookRobDto dto) {
        /**
         * redis、mysql均采用本地部署，并发数增加到20000+发生超卖现象
         * 原因：事务与redis锁的冲突导致，redis释放锁的时候事务并未提交，导致并发量较大时发生超卖现象
         * 优化：将加锁操作放在aop中
         */
        final String key = "robWithRedisLock:" + dto.getBookNo();
        final String value = UUID.randomUUID().toString() + ":" + dto.getBookNo();
        ValueOperations operations = redisTemplate.opsForValue();
        boolean result = false;
        try {
            // 调用SET K V NX PX操作获得锁
            result = operations.setIfAbsent(key, value, 5L, TimeUnit.SECONDS);
            if (result) {
                BookStore bookStore = bookStoreMapper.selectByBookNo(dto.getBookNo());
                // 库存充足且当前用户未抢购过
                if (bookStore != null && bookStore.getStore() > 0) {
                    Integer count = bookRobMapper.countByBookNoUserId(dto.getUserId(), dto.getBookNo());
                    if (count <= 0) {
                        // 更新库存记录
                        int res = bookStoreMapper.updateStore(dto.getBookNo());
                        if (res > 0) {
                            BookRob bookRob = new BookRob();
                            bookRob.setBookNo(dto.getBookNo());
                            bookRob.setUserId(dto.getUserId());
                            bookRob.setCreateTime(LocalDateTime.now());
                            bookRobMapper.insertSelective(bookRob);
                            System.out.println("抢购成功：" + bookRob.toString());
                            return BaseResponse.success("抢购成功");
                        }
                    }
                }
                return BaseResponse.error("抢购结束");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (result) {
                // 释放锁
                String release_lock_lua_script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(release_lock_lua_script, Long.class);
                redisTemplate.execute(redisScript, Collections.singletonList(key), Collections.singletonList(value));
            }
        }
        return BaseResponse.error("抢购失败");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @RedisServiceLock
    public BaseResponse robWithAopRedisLock(BookRobDto dto) {
        BookStore bookStore = bookStoreMapper.selectByBookNo(dto.getBookNo());
        // 库存充足且当前用户未抢购过
        if (bookStore != null && bookStore.getStore() > 0) {
            Integer count = bookRobMapper.countByBookNoUserId(dto.getUserId(), dto.getBookNo());
            if (count <= 0) {
                // 更新库存记录
                int res = bookStoreMapper.updateStore(dto.getBookNo());
                if (res > 0) {
                    BookRob bookRob = new BookRob();
                    bookRob.setBookNo(dto.getBookNo());
                    bookRob.setUserId(dto.getUserId());
                    bookRob.setCreateTime(LocalDateTime.now());
                    bookRobMapper.insertSelective(bookRob);
                    System.out.println("抢购成功：" + bookRob.toString());
                    return BaseResponse.success("抢购成功");
                }
            }
        }
        return BaseResponse.error("抢购结束");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @ZookeeperServiceLock
    public BaseResponse robWithAopZkLock(BookRobDto dto) {
        BookStore bookStore = bookStoreMapper.selectByBookNo(dto.getBookNo());
        // 库存充足且当前用户未抢购过
        if (bookStore != null && bookStore.getStore() > 0) {
            Integer count = bookRobMapper.countByBookNoUserId(dto.getUserId(), dto.getBookNo());
            if (count <= 0) {
                // 更新库存记录
                int res = bookStoreMapper.updateStore(dto.getBookNo());
                if (res > 0) {
                    BookRob bookRob = new BookRob();
                    bookRob.setBookNo(dto.getBookNo());
                    bookRob.setUserId(dto.getUserId());
                    bookRob.setCreateTime(LocalDateTime.now());
                    bookRobMapper.insertSelective(bookRob);
                    System.out.println("抢购成功：" + bookRob.toString());
                    return BaseResponse.success("抢购成功");
                }
            }
        }
        return BaseResponse.error("抢购结束");
    }

    @Autowired
    private RedissonClient redissonClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse robWithRedissonLock(BookRobDto dto) {
        final String lockKey = "robWithRedissonLock:" + dto.getBookNo();
        RLock rLock = redissonClient.getLock(lockKey);
        try {
            // 尝试获取分布式锁，最多等待100秒，超过这个值，则认为获取锁失败，上锁以后10秒以后释放锁
            boolean result = rLock.tryLock(100, 10, TimeUnit.SECONDS);
            if (result) {
                BookStore bookStore = bookStoreMapper.selectByBookNo(dto.getBookNo());
                if (bookStore != null && bookStore.getStore() > 0) {
                    Integer count = bookRobMapper.countByBookNoUserId(dto.getUserId(), dto.getBookNo());
                    if (count <= 0) {
                        // 更新库存记录
                        int res = bookStoreMapper.updateStoreWithLock(dto.getBookNo());
                        if (res > 0) {
                            BookRob bookRob = new BookRob();
                            bookRob.setBookNo(dto.getBookNo());
                            bookRob.setUserId(dto.getUserId());
                            bookRob.setCreateTime(LocalDateTime.now());
                            bookRobMapper.insertSelective(bookRob);
                            System.out.println("抢购成功：" + bookRob.toString());
                            return BaseResponse.success("抢购成功");
                        }
                    }
                }
                return BaseResponse.error("库存不足");
            }
            throw new Exception("redisson获取锁异常");
        } catch (Exception e) {
            return BaseResponse.error("抢购太火爆了，请稍候再试");
        } finally {
            if (rLock != null) {
                // 以lua脚本形式执行删除命令
                rLock.unlock();
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse robWithRedLock(BookRobDto dto) {
        final String lockKey = "robWithRedLock-" + dto.getBookNo();
        Config config1 = new Config();
        config1.useSingleServer().setAddress("redis://192.168.1.156:7000").setDatabase(0);
        RedissonClient redissonClient1 = Redisson.create(config1);
        Config config2 = new Config();
        config2.useSingleServer().setAddress("redis://192.168.1.156:7001").setDatabase(0);
        RedissonClient redissonClient2 = Redisson.create(config2);
        Config config3 = new Config();
        config3.useSingleServer().setAddress("redis://192.168.1.156:7002").setDatabase(0);
        RedissonClient redissonClient3 = Redisson.create(config3);
        RLock lock1 = redissonClient1.getLock(lockKey);
        RLock lock2 = redissonClient2.getLock(lockKey);
        RLock lock3 = redissonClient3.getLock(lockKey);
        // 向3个redis实例尝试加锁
        RedissonRedLock redissonRedLock = new RedissonRedLock(lock1, lock2, lock3);
        boolean isLock;
        try {
            // 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。
            isLock = redissonRedLock.tryLock(500, 10000, TimeUnit.MILLISECONDS);
            if (isLock) {
                BookStore bookStore = bookStoreMapper.selectByBookNo(dto.getBookNo());
                if (bookStore != null && bookStore.getStore() > 0) {
                    Integer count = bookRobMapper.countByBookNoUserId(dto.getUserId(), dto.getBookNo());
                    if (count <= 0) {
                        // 更新库存记录
                        int res = bookStoreMapper.updateStoreWithLock(dto.getBookNo());
                        if (res > 0) {
                            BookRob bookRob = new BookRob();
                            bookRob.setBookNo(dto.getBookNo());
                            bookRob.setUserId(dto.getUserId());
                            bookRob.setCreateTime(LocalDateTime.now());
                            bookRobMapper.insertSelective(bookRob);
                            System.out.println("抢购成功：" + bookRob.toString());
                            return BaseResponse.success("抢购成功");
                        }
                    }
                }
                return BaseResponse.error("库存不足");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 无论如何, 最后都要解锁
            redissonRedLock.unlock();
        }
        return BaseResponse.error("抢购太火爆了，请稍候再试");
    }
}
